Technology overview

Affymetrix microarrays are a popular commercial platform available for a wide range of genomics applications (gene expression profiling, SNP genotyping, ChIP-chip analysis etc.) in different species. The main distinction between Affymetrix and other array technologies is the use of many short (25mer) probes to measure hybridisation. In this practical, we explore the basic analysis issues for Affymetrix GeneChips which measure gene expression using multiple (11-20) perfect match (PM) and mismatch (MM) probes concentrated in the 3’ region of each transcript.

Despite our focus on expression arrays, many of the processing steps, such as quality assessment and normalisation are equally important for other applications of Affymetrix technology to ensure that the signal is comparable between arrays. In this practical we use several Bioconductor packages (affy, affyPLM, limma, etc.) to read in the raw data, assess data quality and normalise an Affymetrix experiment. Raw data for Affymetrix arrays are usually stored in .cel format. We wouldn’t normally wish to view these files directly. These files contain the intensities of each “cell” on chip surface after image analysis.

  • Each hybridisation has a unique cel file
  • It is know in advance which probe / probe set is located within each cell
  • Each chip type (e.g.hgu95av2) has a cdf (chip description) file which is required to decode which probe and probe set is within each cell.

Example analysis in R

The estrogen dataset

The experiment we will analyse is made-up of eight Affymetrix HGU95Av2 GeneChips. The aim of the experiment is briefly described below (excerpt taken from the factDesign package vignette).

“The investigators in this experiment were interested in the effect of estrogen on the genes in ER+ breast cancer cells over time. After serum starvation of all eight samples, they exposed four samples to estrogen, and then measured mRNA transcript abundance after 10 hours for two samples and 48 hours for the other two. They left the remaining four samples untreated, and measured mRNA transcript abundance at 10 hours for two samples, and 48 hours for the other two. Since there are two factors in this experiment (estrogen and time), each at two levels (present or absent, 10 hours or 48 hours), this experiment is said to have a 2 × 2 factorial design.”

The data for this section are described in the estrogen data package in Bioconductor

The cel files for the example experiement are stored in the estrogen directory

The first stage is to read the targets file. In Bioconductor we often use a targets file to define the files corresponding to the raw data for the experiment and which sample groups they belong to. Such a file can be created in a spreadsheet, or text editor, and usually saved as a tab-delimited file. We can have as many columns in the file as we see fit, and one row for each hybridisation. The sample group information is propogated through the analysis and incorporated in the quality assessment and eventually differential expression. We refer to this as the “phenotype data” for the experiment, or sometimes the “metadata”.

Packages and data

If you did not install the affy Bioconductor package, you will need to do so now:-

source("http://www.bioconductor.org/biocLite.R")
biocLite("affy")

The data for the practical can be found in the course zip file

library(affy)
targetsFile <- "estrogen/estrogen.txt"
targetsFile
[1] "estrogen/estrogen.txt"
pd <- read.AnnotatedDataFrame(targetsFile,header=TRUE,sep="",row.names=1)
pData(pd)

As .cel files are not a typical file format, we cannot use the standard R functions such as read.delim, read.csv and read.table. The function to import the data is ReadAffy from the affy package.

raw <-ReadAffy(celfile.path = "estrogen", filenames=rownames(pData(pd)),phenoData = pd)
raw
replacing previous import ‘AnnotationDbi::tail’ by ‘utils::tail’ when loading ‘hgu95av2cdf’replacing previous import ‘AnnotationDbi::head’ by ‘utils::head’ when loading ‘hgu95av2cdf’
AffyBatch object
size of arrays=640x640 features (19 kb)
cdf=HG_U95Av2 (12625 affyids)
number of samples=8
number of genes=12625
annotation=hgu95av2
notes=

What type of Affy array is this How many features? How many samples?


Diagnostic plots

As with other high-throughput technologies, quality assessment is an important part of the analysis process. Data quality can be checked using various diagnostic plots.

The first diagnostic plot we will meet is the “boxplot”, which is a commonly-used plot in data analysis for comparing data distributions. The exact definition of how is boxplot is drawn can vary, the definitions always hold;

  • The bottom of the box is the first quartile; the 25th percentile
  • The top of the box is the third quartile; the 75th percentile
  • The median is represented by a horizontal line
  • The inter-quartile range (IQR) is the difference between the 75th and 25th percentiles

Here, we draw a boxplot to compare two distributions; x with a mean of 0 and standard deviation of 1, and y with a mean of 2 and standard deviation of 4.

x <- rnorm(100)
y <- rnorm(100, mean = 2,sd=4)
boxplot(x,y)

It is also common to draw whiskers that extend to 1.5 X the IQR from the lower and upper quartile. Any points outside this range can be represented by a dot.

In Bioconductor, you will usually find that package authors have created shortcuts to allow complicated data types to be visualised with common functions. For instance, if we want to construct a boxplot from our raw Affymetrix data it would be quite a daunting task for the novice to extract the revelant information from the object. Instead, we can use functions that we are familiar with such as boxplot. The plot can also be customised in ways that we are familiar with such as changing the color (the col argument) and label orientation (las=2 to make labels perpendicular to the axis)

boxplot(raw,col="red",las=2)


What do you notice from this plot?


Perfect-Match and Mismatch probes

With the short DNA sequences used for microarray hybridisation, there is the possibility for the sequence designed to not be specific-enough and hybridise to many regions of the genome. Affymetrix attempt to resolve this issue by having a mismatch sequence corresponding to each probe. Ideally, the subtracting the mismatch from the corresponding perfect match( \(PM - MM\)) should yield a background-corrected, and more-reliable, signal.


Generate histograms of the PM and MM intensities from the first array. Do you notice any difference in the signal distribution of the PMs and MMs?


par(mfrow=c(2,1))
hist(log2(pm(raw[,1])),breaks=100,col="steelblue",main="PM",xlim=c(4,14))
hist(log2(mm(raw[,1])),breaks=100,col="steelblue",main="MM",xlim=c(4,14))

M-A plots are a useful way of comparing the red and green channels from a two-colour microarray. For Affymetrix data, which is a single-channel technology, M and A values can be calculated using the intensities from a pair of chips. We would typically produce a plot using samples from the same biological group, where we would not expect to observe much difference in intensity for a given gene.

i.e. if X\(_i\) is the intensity of a given probe from chip \(i\) and X\(_j\) is the intensity for the same probe on chip \(j\), then \(A = 1/2 (log_2(X_i) + log_2(X_j))\) and \(M = log_2(X_i) − log_2(X_j)\) . In this experiment, there are 8 GeneChips, which gives 28 distinct pair-wise comparisons between arrays. We will focus on a subset of these below.

mva.pairs(pm(raw)[,1:4],plot.method="smoothScatter")

mva.pairs(pm(raw)[,5:8],plot.method="smoothScatter")


Based on all the plots you have generated, what would you conclude about the overall quality of this experiment? ******

Probe-level Linear Models

Probe-level Linear Models (PLMs) can be used as an additional tool to assess relative data quality within an experiment. Many different model specifications are possible, with the simplest fitting chip, and probe effects to the log\(_2\) intensities within each probeset across an experiment in a robust way. The output is a matrix of residuals, or weights for each chip which can be used as an additional diagnostic; systematically high residuals across an entire array, or a large fraction of an array is indicative of an outlier array. The main use of this tool is in deciding whether or not to keep an array in the down-stream data analysis.

Relative Log Expression (RLE)

The Relative Log Expression (RLE) values are computed by calculating for each probe-set the ratio between the expression of a probe-set and the median expression of this probe-set across all arrays of the experiment. It is assumed that most probe-sets are not changed across the arrays, so it is expected that these ratios are around 0 on a log scale. The boxplots presenting the distribution of these log-ratios should then be centered near 0 and have similar spread. Other behavior would be a sign of low quality.

Normalized Unscaled Standard Error (NUSE)

The Normalized Unscaled Standard Error (NUSE) is the individual probe error fitting the Probe-Level Model (the PLM models expression measures using a M-estimator robust regression). The NUSE values are standardized at the probe-set level across the arrays: median values for each probe-set are set to 1. The boxplots allow checking (1) if all distributions are centered near 1 (typically an array with a boxplot centered around 1.1 shows bad quality) and (2) if one array has globally higher spread of NUSE distribution than others, which may also be a sign of low quality.

library(affyPLM)
Loading required package: gcrma
Loading required package: preprocessCore
plmset <- fitPLM(raw)
NUSE(plmset,las=2)

RLE(plmset,las=2)

We can look at the images of the array surface. Ocassionally this can reveal problems on the array. See this gallery of interesting examples. Affymetrix, and other older arrays are vunerable to spatial artefacts as the same probe set is found in the same location on each chip.

If you want to look at an example of a bad array image, we can use the following code;

bad <- ReadAffy(celfile.path = "estrogen/",filenames="bad.cel")
image(bad)

Try out some images for this dataset

par(mfrow=c(2,4))
image(raw[,1])
image(raw[,2])
image(raw[,3])
image(raw[,4])
image(raw[,5])
image(raw[,6])
image(raw[,7])
image(raw[,8])

  • Do you see any problems?

If we are happy with the quality of the raw data we can proceed to the next step. So far in the data, we have multiple probes within a probeset, and a perfect and mismatch measurement for each probe. These are not particular covenient values for statistical analysis as we would like a single measurement for each gene for each sample (/hybridisation).

Summarising and Normalising the Estrogen dataset

Many normalisation and summarisation methods have been developed for Affymetrix data. These include MAS5.0 and PLIER which were developed by Affymetrix, and RMA, GC-RMA, dChip and vsn (to name but a few) which have been developed by academic researchers. Many of these methods are available in the affy package. For a comparison of some of these methods and assessment of their performance on different control data sets, see Bolstad et al or Millenaar et al. In this practical we will use the RMA (Robust Multichip Averaging) method described in Irizarry et al.

Procedure;

  • model based background adjustment
  • followed by quantile normalisation and a
  • robust summary method (median polish) on the log\(_2\) PM intensities
  • to obtain probeset summary values.

Normalisation

  • We want to be observing biological and not technical variation
  • We wouldn’t expect such wholesale changes on a per-sample basis
  • Easy option would to scale values for each array to median level

  • What would happen if we had hybridised all the estrogen-treated samples on the first four arrays, and un-treated on the last four - Could you analyse the data?

  • Genes on array 2 are on average 2.6 lower than the global median, so add 2.6 to each gene
  • Genes on array 8 are on average 0.7 higher than the global median, so subtract 0.7 from each gene
  • etc

Non-linear effects

As we saw before The MA-plot is commonly-used in microarray analysis to commonly-used to visualise differences between pairs of arrays. These plots can reveal non-linear effects and motivate more-sophisticated methods. On older array technologies, it was typical to see a banana-shaped trend on these plots.

(not our dataset)

Quantile normalisation

This is arguably the most-popular normalisation method available. Consider the following matrix of values to be normalised

     Array1  Array2  Array3 
[1,] "Rank4" "Rank5" "Rank3"
[2,] "Rank2" "Rank4" "Rank2"
[3,] "Rank3" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "Rank1" "Rank2" "Rank5"

Sort each column Smallest…Largest

Sorted data

apply(raw.values, 2,sort)
     Array1 Array2 Array3
[1,]   0.18   1.60   2.88
[2,]   0.72   1.65   3.12
[3,]   1.38   2.43   4.37
[4,]   2.19   2.92   4.93
[5,]   2.32   3.04   5.89

Then calculate target distribution by averaging the sorted rows

Rank1 Rank2 Rank3 Rank4 Rank5 
1.553 1.830 2.727 3.347 3.750 

Go back to the rank matrix

     Array1  Array2  Array3 
[1,] "Rank4" "Rank5" "Rank3"
[2,] "Rank2" "Rank4" "Rank2"
[3,] "Rank3" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "Rank1" "Rank2" "Rank5"

Substitute with values from the target distribution

Rank1 Rank2 Rank3 Rank4 Rank5 
1.553 1.830 2.727 3.347 3.750 
ranked.values[,1] <- gsub("Rank1",target["Rank1"],ranked.values[,1])
ranked.values
     Array1  Array2  Array3 
[1,] "Rank4" "Rank5" "Rank3"
[2,] "Rank2" "Rank4" "Rank2"
[3,] "Rank3" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "1.553" "Rank2" "Rank5"
ranked.values[,1] <- gsub("Rank2",target["Rank2"],ranked.values[,1])
ranked.values
     Array1  Array2  Array3 
[1,] "Rank4" "Rank5" "Rank3"
[2,] "1.83"  "Rank4" "Rank2"
[3,] "Rank3" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "1.553" "Rank2" "Rank5"
ranked.values[,1] <- gsub("Rank3",target["Rank3"],ranked.values[,1])
ranked.values
     Array1  Array2  Array3 
[1,] "Rank4" "Rank5" "Rank3"
[2,] "1.83"  "Rank4" "Rank2"
[3,] "2.727" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "1.553" "Rank2" "Rank5"
ranked.values[,1] <- gsub("Rank4",target["Rank4"],ranked.values[,1])
ranked.values
     Array1  Array2  Array3 
[1,] "3.347" "Rank5" "Rank3"
[2,] "1.83"  "Rank4" "Rank2"
[3,] "2.727" "Rank3" "Rank1"
[4,] "Rank5" "Rank1" "Rank4"
[5,] "1.553" "Rank2" "Rank5"
ranked.values[,1] <- gsub("Rank5",target["Rank5"],ranked.values[,1])
ranked.values
     Array1  Array2  Array3 
[1,] "3.347" "Rank5" "Rank3"
[2,] "1.83"  "Rank4" "Rank2"
[3,] "2.727" "Rank3" "Rank1"
[4,] "3.75"  "Rank1" "Rank4"
[5,] "1.553" "Rank2" "Rank5"
for(i in 1:3){
ranked.values[,i] <- gsub("Rank1",target["Rank1"],ranked.values[,i])
ranked.values[,i] <- gsub("Rank2",target["Rank2"],ranked.values[,i])
ranked.values[,i] <- gsub("Rank3",target["Rank3"],ranked.values[,i])
ranked.values[,i] <- gsub("Rank4",target["Rank4"],ranked.values[,i])
ranked.values[,i] <- gsub("Rank5",target["Rank5"],ranked.values[,i])
}
ranked.values <- as.data.frame(ranked.values)
rownames(ranked.values) <- rownames(raw.values)
raw.values
ranked.values

Final Code

The procedure can be encapsulated in relatively few lines of code

ranked.values <- apply(raw.values, 2, function(x) paste("Rank",
      rank(x,ties.method="min"),sep=""))
target <- round(rowMeans(apply(df, 2,sort)),3)
names(target) <- paste("Rank", 1:length(target),sep="")
for(i in 1:ncol(raw.values)){
  for(nm in names(target)){
    ranked.values[,i] <- gsub(nm,target[nm],ranked.values[,i])  
      }
}
norm <- as.data.frame(ranked.values)

We can use the normalizeQuantiles function within the limma package to do the normalisation.

library(limma)
?normalizeQuantiles

Caveats

  • Distributions of samples are expected to be the same
  • Majority of genes do not change between groups
    • might be violated if you have samples that are dramatically altered
    • e.g. low number of genes in a screening panel that are expected to change

Other normalisation procedures

  • loess or splines
    • need a reference or “average” array to compare
    • estimate the curve and use to correct each point
library(affy)
?normalize.loess

Running RMA on our dataset

The affy package provides lots of different ways to process raw Affymetricx data, one of which is an implementation of RMA. For other methods, see the expresso function or package Vignette

?expresso
vignette("affy")

The result is an ExpressionSet, which is a ubiquitous object in Biooconductor for storing high-throughput data. Later-on in the course will we import data from public repositories, and these data will often be in a normalised form.

eset <- rma(raw)
Background correcting
Normalizing
Calculating Expression
eset
ExpressionSet (storageMode: lockedEnvironment)
assayData: 12625 features, 8 samples 
  element names: exprs 
protocolData
  sampleNames: low10-1.cel low10-2.cel ... high48-2.cel (8 total)
  varLabels: ScanDate
  varMetadata: labelDescription
phenoData
  sampleNames: low10-1.cel low10-2.cel ... high48-2.cel (8 total)
  varLabels: estrogen time.h
  varMetadata: labelDescription
featureData: none
experimentData: use 'experimentData(object)'
Annotation: hgu95av2 

The ExpressionSet is a convenient object-type for storing multiple high-dimensional data frames. The structure of the object is much more complicated than other types of data we have seen before. In fact we do not need to know need exactly how the object is constructed internally. The package authors have provided convenient functions that will allow us to access the data.

For example, if we want to extract the expression measurements themselves the function to use is called exprs. The result will be a matrix with one row for each gene, and one column for each sample.

head(exprs(eset))
          low10-1.cel low10-2.cel high10-1.cel high10-2.cel low48-1.cel low48-2.cel high48-1.cel
100_g_at     9.642896    9.741496     9.537036     9.353625    9.591697    9.570590     9.475796
1000_at     10.398169   10.254362    10.003971     9.903528   10.374866   10.033520    10.345066
1001_at      5.717613    5.881008     5.859563     5.954028    5.960540    6.020889     5.981080
1002_f_at    5.512596    5.801807     5.571065     5.608132    5.390064    5.494511     5.508104
1003_s_at    7.783927    8.007975     8.037999     7.835120    7.926487    8.138870     7.994937
1004_at      7.289162    7.603670     7.488539     7.771506    7.521789    7.599544     7.456149
          high48-2.cel
100_g_at      9.530655
1000_at       9.863321
1001_at       6.285192
1002_f_at     5.630107
1003_s_at     8.233338
1004_at       7.675171
summary(exprs(eset))
  low10-1.cel      low10-2.cel      high10-1.cel     high10-2.cel     low48-1.cel    
 Min.   : 4.269   Min.   : 4.280   Min.   : 4.294   Min.   : 4.135   Min.   : 4.238  
 1st Qu.: 5.601   1st Qu.: 5.628   1st Qu.: 5.592   1st Qu.: 5.621   1st Qu.: 5.630  
 Median : 7.108   Median : 7.123   Median : 7.108   Median : 7.123   Median : 7.102  
 Mean   : 7.295   Mean   : 7.295   Mean   : 7.293   Mean   : 7.293   Mean   : 7.291  
 3rd Qu.: 8.600   3rd Qu.: 8.592   3rd Qu.: 8.616   3rd Qu.: 8.607   3rd Qu.: 8.588  
 Max.   :14.736   Max.   :14.763   Max.   :14.765   Max.   :14.733   Max.   :14.750  
  low48-2.cel      high48-1.cel     high48-2.cel   
 Min.   : 4.306   Min.   : 4.273   Min.   : 4.327  
 1st Qu.: 5.636   1st Qu.: 5.591   1st Qu.: 5.626  
 Median : 7.094   Median : 7.063   Median : 7.078  
 Mean   : 7.267   Mean   : 7.287   Mean   : 7.257  
 3rd Qu.: 8.570   3rd Qu.: 8.598   3rd Qu.: 8.571  
 Max.   :14.761   Max.   :14.706   Max.   :14.716  

You will notice the rma also incorporates a log\(_2\) transformation. The values present in the cel files are derived from processing the TIFF images of the chip surface, which are on the scale 0 - 2^16. However, this is not a very convenient scale for visualisation and analysis as most of the observations are found at the lower end of the scale and there is a much greater variance at the higer values. We say that the data exhibit ‘heteroscedasticity’. To alleviate this we can use a data transformation such as log\(_2\) and afterwards our data will be in the range 0 to 16. Another method you may come across is vsn.

boxplot(exprs(eset),las=2)


How can you tell from the boxplot that these data have been normalised?


An MA-plot can be repeated on the normalised data to verify that the normalisation procedure has been sucessful.

mva.pairs(exprs(eset)[,1:4],log.it = FALSE,plot.method="smoothScatter")

If we have used a targets file to read the raw data, this information will stored in the summarised object. Commonly-referred to as “pheno data”, this can retrieved using the pData function. A nice property of this data frame is that the rows are arranged in the same order as the columns of the expression matrix. i.e. The first column of the expression matrix is described in the first row of the pheno data matrix, and so on.

head(pData(eset))
colnames(exprs(eset))
[1] "low10-1.cel"  "low10-2.cel"  "high10-1.cel" "high10-2.cel" "low48-1.cel"  "low48-2.cel" 
[7] "high48-1.cel" "high48-2.cel"
rownames(pData(eset))
[1] "low10-1.cel"  "low10-2.cel"  "high10-1.cel" "high10-2.cel" "low48-1.cel"  "low48-2.cel" 
[7] "high48-1.cel" "high48-2.cel"

Clustering techniques and PCA that we will meet later in the course can also inform decisions about experiment quality by providing a way of visualing relationships between sample groups.

Automated production of QC plots

The arrayQualityMetrics package is able to produce QA plots from set of cel files, or a normalised dataset.

library(arrayQualityMetrics)
arrayQualityMetrics(eset)

Thoughts on Quality Assessment

  • It is difficult to come up with definitive rules about when to reject a sample from the analysis
  • Try and keep as much information from the lab
    • RIN (RNA Integrity Numbers), tumour composition
  • If we try and reject samples for looking different, we might bias our results and miss interesting findings
  • If the majority of our samples are poor quality, the better quality ones might stand-out as outliers!
  • Try and collect as many samples / replicates as possible to be tolerant to variation

Summary

  • Affy data come in .cel files that can be imported with the affy package
  • QC checks on the raw data include;
    • check the chip image
    • probe-level models
    • boxplots
  • Affy data need to be summarised before further analysis
    • rma (and variants thereof) is most-popular
  • Affy data can be summarised into a common Bioconductor object-type
    • The “ExpressionSet
  • The ExpressionSet, or derivaties thereof, is used throughout Bioconductor to represent all types of high-throughput data
    • methylation, ChIP, RNA-seq
    • basically anything where you can have some kind of genomic measure as rows, and samples as columns
LS0tCnRpdGxlOiAiVGhlIEFmZnltZXRyaXggQW5hbHlzaXMgV29ya2Zsb3ciCmF1dGhvcjogTWFyayBEdW5uaW5nLCBiYXNlZCBvbiBtYXRlcmlhbHMgYnkgQmVuaWx0b24gQ2FydmFsaG8Kb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCgohW10oaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy8yLzIyL0FmZnltZXRyaXgtbWljcm9hcnJheS5qcGcpCgojIFRlY2hub2xvZ3kgb3ZlcnZpZXcKCkFmZnltZXRyaXggbWljcm9hcnJheXMgYXJlIGEgcG9wdWxhciBjb21tZXJjaWFsIHBsYXRmb3JtIGF2YWlsYWJsZSBmb3IgYSB3aWRlIHJhbmdlIG9mCmdlbm9taWNzIGFwcGxpY2F0aW9ucyAoZ2VuZSBleHByZXNzaW9uIHByb2ZpbGluZywgU05QIGdlbm90eXBpbmcsIENoSVAtY2hpcCBhbmFseXNpcyBldGMuKQppbiBkaWZmZXJlbnQgc3BlY2llcy4KVGhlIG1haW4gZGlzdGluY3Rpb24gYmV0d2VlbiBBZmZ5bWV0cml4IGFuZCBvdGhlciBhcnJheSB0ZWNobm9sb2dpZXMgaXMgdGhlIHVzZSBvZiBtYW55CnNob3J0ICgyNW1lcikgcHJvYmVzIHRvIG1lYXN1cmUgaHlicmlkaXNhdGlvbi4KSW4gdGhpcyBwcmFjdGljYWwsIHdlIGV4cGxvcmUgdGhlIGJhc2ljIGFuYWx5c2lzIGlzc3VlcyBmb3IgQWZmeW1ldHJpeCBHZW5lQ2hpcHMgd2hpY2ggbWVhc3VyZSBnZW5lIGV4cHJlc3Npb24gdXNpbmcgbXVsdGlwbGUgKDExLTIwKSBwZXJmZWN0IG1hdGNoIChQTSkgYW5kIG1pc21hdGNoIChNTSkgcHJvYmVzCmNvbmNlbnRyYXRlZCBpbiB0aGUgM+KAmSByZWdpb24gb2YgZWFjaCB0cmFuc2NyaXB0LiAKCiFbXShpbWFnZXMvcG0ucG5nKQoKRGVzcGl0ZSBvdXIgZm9jdXMgb24gZXhwcmVzc2lvbiBhcnJheXMsIG1hbnkKb2YgdGhlIHByb2Nlc3Npbmcgc3RlcHMsIHN1Y2ggYXMgcXVhbGl0eSBhc3Nlc3NtZW50IGFuZCBub3JtYWxpc2F0aW9uIGFyZSBlcXVhbGx5IGltcG9ydGFudApmb3Igb3RoZXIgYXBwbGljYXRpb25zIG9mIEFmZnltZXRyaXggdGVjaG5vbG9neSB0byBlbnN1cmUgdGhhdCB0aGUgc2lnbmFsIGlzIGNvbXBhcmFibGUgYmV0d2VlbiBhcnJheXMuCkluIHRoaXMgcHJhY3RpY2FsIHdlIHVzZSBzZXZlcmFsIEJpb2NvbmR1Y3RvciBwYWNrYWdlcyAoW2FmZnldKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2FmZnkuaHRtbCksIFthZmZ5UExNXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9hZmZ5UExNLmh0bWwpLCBbbGltbWFdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2xpbW1hLmh0bWwpLCBldGMuKSB0byByZWFkIGluCnRoZSByYXcgZGF0YSwgYXNzZXNzIGRhdGEgcXVhbGl0eSBhbmQgbm9ybWFsaXNlIGFuIEFmZnltZXRyaXggZXhwZXJpbWVudC4gUmF3IGRhdGEgZm9yIEFmZnltZXRyaXggYXJyYXlzIGFyZSB1c3VhbGx5IHN0b3JlZCBpbiBbYC5jZWxgXShodHRwOi8vZGVwdC5zdGF0LmxzYS51bWljaC5lZHUvfmtzaGVkZGVuL0NvdXJzZXMvU3RhdDU0NS9Ob3Rlcy9BZmZ4RmlsZUZvcm1hdHMvY2VsLmh0bWwpIGZvcm1hdC4gV2Ugd291bGRuJ3Qgbm9ybWFsbHkgd2lzaCB0byB2aWV3IHRoZXNlIGZpbGVzIGRpcmVjdGx5LiBUaGVzZSBmaWxlcyBjb250YWluIHRoZSBpbnRlbnNpdGllcyBvZiBlYWNoICJjZWxsIiBvbiBjaGlwIHN1cmZhY2UgYWZ0ZXIgaW1hZ2UgYW5hbHlzaXMuIAoKICAtIEVhY2ggaHlicmlkaXNhdGlvbiBoYXMgYSB1bmlxdWUgKmNlbCogZmlsZQogIC0gSXQgaXMga25vdyBpbiBhZHZhbmNlIHdoaWNoIHByb2JlIC8gcHJvYmUgc2V0IGlzIGxvY2F0ZWQgd2l0aGluIGVhY2ggY2VsbAogIC0gRWFjaCBjaGlwIHR5cGUgKGUuZy5oZ3U5NWF2MikgaGFzIGEgKmNkZiogKGNoaXAgZGVzY3JpcHRpb24pIGZpbGUgd2hpY2ggaXMgcmVxdWlyZWQgdG8gZGVjb2RlIHdoaWNoIHByb2JlIGFuZCBwcm9iZSBzZXQgaXMgd2l0aGluIGVhY2ggY2VsbC4KCiMgRXhhbXBsZSBhbmFseXNpcyBpbiBSCgojIyBUaGUgZXN0cm9nZW4gZGF0YXNldAoKVGhlIGV4cGVyaW1lbnQgd2Ugd2lsbCBhbmFseXNlIGlzIG1hZGUtdXAgb2YgZWlnaHQgQWZmeW1ldHJpeCAqSEdVOTVBdjIqCkdlbmVDaGlwcy4gVGhlIGFpbSBvZiB0aGUgZXhwZXJpbWVudCBpcyBicmllZmx5IGRlc2NyaWJlZCBiZWxvdyAoZXhjZXJwdCB0YWtlbiBmcm9tIHRoZQpmYWN0RGVzaWduIHBhY2thZ2UgdmlnbmV0dGUpLgoKPiDigJxUaGUgaW52ZXN0aWdhdG9ycyBpbiB0aGlzIGV4cGVyaW1lbnQgd2VyZSBpbnRlcmVzdGVkIGluIHRoZSBlZmZlY3Qgb2YgZXN0cm9nZW4gb24gdGhlIGdlbmVzIGluIEVSKyBicmVhc3QgY2FuY2VyIGNlbGxzIG92ZXIgdGltZS4gQWZ0ZXIgc2VydW0gc3RhcnZhdGlvbiBvZiBhbGwgZWlnaHQgc2FtcGxlcywgdGhleSBleHBvc2VkCmZvdXIgc2FtcGxlcyB0byBlc3Ryb2dlbiwgYW5kIHRoZW4gbWVhc3VyZWQgbVJOQSB0cmFuc2NyaXB0IGFidW5kYW5jZSBhZnRlciAxMCBob3VycyBmb3IKdHdvIHNhbXBsZXMgYW5kIDQ4IGhvdXJzIGZvciB0aGUgb3RoZXIgdHdvLiBUaGV5IGxlZnQgdGhlIHJlbWFpbmluZyBmb3VyIHNhbXBsZXMgdW50cmVhdGVkLAphbmQgbWVhc3VyZWQgbVJOQSB0cmFuc2NyaXB0IGFidW5kYW5jZSBhdCAxMCBob3VycyBmb3IgdHdvIHNhbXBsZXMsIGFuZCA0OCBob3VycyBmb3IKdGhlIG90aGVyIHR3by4gU2luY2UgdGhlcmUgYXJlIHR3byBmYWN0b3JzIGluIHRoaXMgZXhwZXJpbWVudCAoZXN0cm9nZW4gYW5kIHRpbWUpLCBlYWNoIGF0CnR3byBsZXZlbHMgKHByZXNlbnQgb3IgYWJzZW50LCAxMCBob3VycyBvciA0OCBob3VycyksIHRoaXMgZXhwZXJpbWVudCBpcyBzYWlkIHRvIGhhdmUgYSAyIMOXIDIKZmFjdG9yaWFsIGRlc2lnbi7igJ0KClRoZSBkYXRhIGZvciB0aGlzIHNlY3Rpb24gYXJlIGRlc2NyaWJlZCBpbiB0aGUgW2VzdHJvZ2VuIGRhdGEgcGFja2FnZV0oaHR0cDovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvZGF0YS9leHBlcmltZW50L2h0bWwvZXN0cm9nZW4uaHRtbCkgaW4gQmlvY29uZHVjdG9yCgpUaGUgY2VsIGZpbGVzIGZvciB0aGUgZXhhbXBsZSBleHBlcmllbWVudCBhcmUgc3RvcmVkIGluIHRoZSBgZXN0cm9nZW5gIGRpcmVjdG9yeQoKVGhlIGZpcnN0IHN0YWdlIGlzIHRvIHJlYWQgdGhlICp0YXJnZXRzKiBmaWxlLiBJbiBCaW9jb25kdWN0b3Igd2Ugb2Z0ZW4gdXNlIGEgdGFyZ2V0cyBmaWxlIHRvIGRlZmluZSB0aGUgZmlsZXMgY29ycmVzcG9uZGluZyB0byB0aGUgcmF3IGRhdGEgZm9yIHRoZSBleHBlcmltZW50IGFuZCB3aGljaCBzYW1wbGUgZ3JvdXBzIHRoZXkgYmVsb25nIHRvLiBTdWNoIGEgZmlsZSBjYW4gYmUgY3JlYXRlZCBpbiBhIHNwcmVhZHNoZWV0LCBvciB0ZXh0IGVkaXRvciwgYW5kIHVzdWFsbHkgc2F2ZWQgYXMgYSB0YWItZGVsaW1pdGVkIGZpbGUuIFdlIGNhbiBoYXZlIGFzIG1hbnkgY29sdW1ucyBpbiB0aGUgZmlsZSBhcyB3ZSBzZWUgZml0LCBhbmQgb25lIHJvdyBmb3IgZWFjaCBoeWJyaWRpc2F0aW9uLiBUaGUgc2FtcGxlIGdyb3VwIGluZm9ybWF0aW9uIGlzIHByb3BvZ2F0ZWQgdGhyb3VnaCB0aGUgYW5hbHlzaXMgYW5kIGluY29ycG9yYXRlZCBpbiB0aGUgcXVhbGl0eSBhc3Nlc3NtZW50IGFuZCBldmVudHVhbGx5IGRpZmZlcmVudGlhbCBleHByZXNzaW9uLiBXZSByZWZlciB0byB0aGlzIGFzIHRoZSAicGhlbm90eXBlIGRhdGEiIGZvciB0aGUgZXhwZXJpbWVudCwgb3Igc29tZXRpbWVzIHRoZSAqIm1ldGFkYXRhIiouCgojIyMgUGFja2FnZXMgYW5kIGRhdGEgCgpJZiB5b3UgZGlkIG5vdCBpbnN0YWxsIHRoZSBgYWZmeWAgQmlvY29uZHVjdG9yIHBhY2thZ2UsIHlvdSB3aWxsIG5lZWQgdG8gZG8gc28gbm93Oi0KCmBgYHtyIGV2YWw9RkFMU0V9CnNvdXJjZSgiaHR0cDovL3d3dy5iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQpiaW9jTGl0ZSgiYWZmeSIpCmBgYAoKVGhlIGRhdGEgZm9yIHRoZSBwcmFjdGljYWwgY2FuIGJlIGZvdW5kIGluIHRoZSBbY291cnNlIHppcCBmaWxlXShodHRwczovL3Jhd2dpdC5jb20vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcvbWljcm9hcnJheS1hbmFseXNpcy9tYXN0ZXIvQ291cnNlX0RhdGEuemlwKQoKYGBge3J9CmxpYnJhcnkoYWZmeSkKdGFyZ2V0c0ZpbGUgPC0gImVzdHJvZ2VuL2VzdHJvZ2VuLnR4dCIKdGFyZ2V0c0ZpbGUKcGQgPC0gcmVhZC5Bbm5vdGF0ZWREYXRhRnJhbWUodGFyZ2V0c0ZpbGUsaGVhZGVyPVRSVUUsc2VwPSIiLHJvdy5uYW1lcz0xKQpwRGF0YShwZCkKYGBgCgoKCkFzIGAuY2VsYCBmaWxlcyBhcmUgbm90IGEgdHlwaWNhbCBmaWxlIGZvcm1hdCwgd2UgY2Fubm90IHVzZSB0aGUgc3RhbmRhcmQgUiBmdW5jdGlvbnMgc3VjaCBhcyBgcmVhZC5kZWxpbWAsIGByZWFkLmNzdmAgYW5kIGByZWFkLnRhYmxlYC4gVGhlIGZ1bmN0aW9uIHRvIGltcG9ydCB0aGUgZGF0YSBpcyBgUmVhZEFmZnlgIGZyb20gdGhlIFthZmZ5XShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9hZmZ5Lmh0bWwpIHBhY2thZ2UuIAoKYGBge3J9CgpyYXcgPC1SZWFkQWZmeShjZWxmaWxlLnBhdGggPSAiZXN0cm9nZW4iLCBmaWxlbmFtZXM9cm93bmFtZXMocERhdGEocGQpKSxwaGVub0RhdGEgPSBwZCkKcmF3CmBgYAoKKioqKioqCldoYXQgdHlwZSBvZiBBZmZ5IGFycmF5IGlzIHRoaXMKSG93IG1hbnkgZmVhdHVyZXM/CkhvdyBtYW55IHNhbXBsZXM/CgoqKioqKioKCgoKCiMjIERpYWdub3N0aWMgcGxvdHMgCgpBcyB3aXRoIG90aGVyIGhpZ2gtdGhyb3VnaHB1dCB0ZWNobm9sb2dpZXMsIHF1YWxpdHkgYXNzZXNzbWVudCBpcyBhbiBpbXBvcnRhbnQgcGFydCBvZiB0aGUKYW5hbHlzaXMgcHJvY2Vzcy4gRGF0YSBxdWFsaXR5IGNhbiBiZSBjaGVja2VkIHVzaW5nIHZhcmlvdXMgZGlhZ25vc3RpYyBwbG90cy4KClRoZSBmaXJzdCBkaWFnbm9zdGljIHBsb3Qgd2Ugd2lsbCBtZWV0IGlzIHRoZSAiW2JveHBsb3RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0JveF9wbG90KSIsIHdoaWNoIGlzIGEgY29tbW9ubHktdXNlZCBwbG90IGluIGRhdGEgYW5hbHlzaXMgZm9yICpjb21wYXJpbmcgZGF0YSBkaXN0cmlidXRpb25zKi4gVGhlIGV4YWN0IGRlZmluaXRpb24gb2YgaG93IGlzIGJveHBsb3QgaXMgZHJhd24gY2FuIHZhcnksIHRoZSBkZWZpbml0aW9ucyBhbHdheXMgaG9sZDsKCi0gVGhlIGJvdHRvbSBvZiB0aGUgYm94IGlzIHRoZSAqZmlyc3QgcXVhcnRpbGUqOyB0aGUgMjV0aCBwZXJjZW50aWxlCi0gVGhlIHRvcCBvZiB0aGUgYm94IGlzIHRoZSAqdGhpcmQgcXVhcnRpbGUqOyB0aGUgNzV0aCBwZXJjZW50aWxlCi0gVGhlIG1lZGlhbiBpcyByZXByZXNlbnRlZCBieSBhIGhvcml6b250YWwgbGluZQotIFRoZSAqaW50ZXItcXVhcnRpbGUgcmFuZ2UqIChJUVIpIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIDc1dGggYW5kIDI1dGggcGVyY2VudGlsZXMKCkhlcmUsIHdlIGRyYXcgYSBib3hwbG90IHRvIGNvbXBhcmUgdHdvIGRpc3RyaWJ1dGlvbnM7IGB4YCB3aXRoIGEgbWVhbiBvZiAwIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMSwgYW5kIGB5YCB3aXRoIGEgbWVhbiBvZiAyIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgNC4KCmBgYHtyfQp4IDwtIHJub3JtKDEwMCkKeSA8LSBybm9ybSgxMDAsIG1lYW4gPSAyLHNkPTQpCmJveHBsb3QoeCx5KQpgYGAKCkl0IGlzIGFsc28gY29tbW9uIHRvIGRyYXcgd2hpc2tlcnMgdGhhdCBleHRlbmQgdG8gMS41IFggdGhlIElRUiBmcm9tIHRoZSBsb3dlciBhbmQgdXBwZXIgcXVhcnRpbGUuIEFueSBwb2ludHMgb3V0c2lkZSB0aGlzIHJhbmdlIGNhbiBiZSByZXByZXNlbnRlZCBieSBhIGRvdC4KCgpJbiBCaW9jb25kdWN0b3IsIHlvdSB3aWxsIHVzdWFsbHkgZmluZCB0aGF0IHBhY2thZ2UgYXV0aG9ycyBoYXZlIGNyZWF0ZWQgc2hvcnRjdXRzIHRvIGFsbG93IGNvbXBsaWNhdGVkIGRhdGEgdHlwZXMgdG8gYmUgdmlzdWFsaXNlZCB3aXRoIGNvbW1vbiBmdW5jdGlvbnMuIEZvciBpbnN0YW5jZSwgaWYgd2Ugd2FudCB0byBjb25zdHJ1Y3QgYSBib3hwbG90IGZyb20gb3VyIHJhdyBBZmZ5bWV0cml4IGRhdGEgaXQgd291bGQgYmUgcXVpdGUgYSBkYXVudGluZyB0YXNrIGZvciB0aGUgbm92aWNlIHRvIGV4dHJhY3QgdGhlIHJldmVsYW50IGluZm9ybWF0aW9uIGZyb20gdGhlIG9iamVjdC4gSW5zdGVhZCwgd2UgY2FuIHVzZSBmdW5jdGlvbnMgdGhhdCB3ZSBhcmUgZmFtaWxpYXIgd2l0aCBzdWNoIGFzIGBib3hwbG90YC4gVGhlIHBsb3QgY2FuIGFsc28gYmUgY3VzdG9taXNlZCBpbiB3YXlzIHRoYXQgd2UgYXJlIGZhbWlsaWFyIHdpdGggc3VjaCBhcyBjaGFuZ2luZyB0aGUgY29sb3IgKHRoZSBgY29sYCBhcmd1bWVudCkgYW5kIGxhYmVsIG9yaWVudGF0aW9uIChgbGFzPTJgIHRvIG1ha2UgbGFiZWxzIHBlcnBlbmRpY3VsYXIgdG8gdGhlIGF4aXMpCgpgYGB7cn0KYm94cGxvdChyYXcsY29sPSJyZWQiLGxhcz0yKQpgYGAKCioqKioqKgoKIyMjIFdoYXQgZG8geW91IG5vdGljZSBmcm9tIHRoaXMgcGxvdD8KCioqKioqKgoKCiMjIFBlcmZlY3QtTWF0Y2ggYW5kIE1pc21hdGNoIHByb2JlcwoKV2l0aCB0aGUgc2hvcnQgRE5BIHNlcXVlbmNlcyB1c2VkIGZvciBtaWNyb2FycmF5IGh5YnJpZGlzYXRpb24sIHRoZXJlIGlzIHRoZSBwb3NzaWJpbGl0eSBmb3IgdGhlIHNlcXVlbmNlIGRlc2lnbmVkIHRvIG5vdCBiZSBzcGVjaWZpYy1lbm91Z2ggYW5kIGh5YnJpZGlzZSB0byBtYW55IHJlZ2lvbnMgb2YgdGhlIGdlbm9tZS4gQWZmeW1ldHJpeCBhdHRlbXB0IHRvIHJlc29sdmUgdGhpcyBpc3N1ZSBieSBoYXZpbmcgYSBtaXNtYXRjaCBzZXF1ZW5jZSBjb3JyZXNwb25kaW5nIHRvIGVhY2ggcHJvYmUuIElkZWFsbHksIHRoZSBzdWJ0cmFjdGluZyB0aGUgbWlzbWF0Y2ggZnJvbSB0aGUgY29ycmVzcG9uZGluZyBwZXJmZWN0IG1hdGNoKCAkUE0gLSBNTSQpIHNob3VsZCB5aWVsZCBhIGJhY2tncm91bmQtY29ycmVjdGVkLCBhbmQgbW9yZS1yZWxpYWJsZSwgc2lnbmFsLgoKCioqKioqKgoKR2VuZXJhdGUgaGlzdG9ncmFtcyBvZiB0aGUgUE0gYW5kIE1NIGludGVuc2l0aWVzIGZyb20gdGhlIGZpcnN0IGFycmF5LiBEbyB5b3UKbm90aWNlIGFueSBkaWZmZXJlbmNlIGluIHRoZSBzaWduYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSBQTXMgYW5kIE1Ncz8KCioqKioqKgoKYGBge3J9CnBhcihtZnJvdz1jKDIsMSkpCmhpc3QobG9nMihwbShyYXdbLDFdKSksYnJlYWtzPTEwMCxjb2w9InN0ZWVsYmx1ZSIsbWFpbj0iUE0iLHhsaW09Yyg0LDE0KSkKaGlzdChsb2cyKG1tKHJhd1ssMV0pKSxicmVha3M9MTAwLGNvbD0ic3RlZWxibHVlIixtYWluPSJNTSIseGxpbT1jKDQsMTQpKQoKYGBgCgoKCioqKk0tQSoqKiBwbG90cyBhcmUgYSB1c2VmdWwgd2F5IG9mIGNvbXBhcmluZyB0aGUgcmVkIGFuZCBncmVlbiBjaGFubmVscyBmcm9tIGEgdHdvLWNvbG91cgptaWNyb2FycmF5LiBGb3IgQWZmeW1ldHJpeCBkYXRhLCB3aGljaCBpcyBhIHNpbmdsZS1jaGFubmVsIHRlY2hub2xvZ3ksIE0gYW5kIEEgdmFsdWVzIGNhbgpiZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBpbnRlbnNpdGllcyBmcm9tIGEgcGFpciBvZiBjaGlwcy4gV2Ugd291bGQgdHlwaWNhbGx5IHByb2R1Y2UgYSBwbG90IHVzaW5nIHNhbXBsZXMgZnJvbSB0aGUgc2FtZSBiaW9sb2dpY2FsIGdyb3VwLCB3aGVyZSB3ZSB3b3VsZCBub3QgZXhwZWN0IHRvIG9ic2VydmUgbXVjaCBkaWZmZXJlbmNlIGluIGludGVuc2l0eSBmb3IgYSBnaXZlbiBnZW5lLgoKaS5lLiBpZiBYJF9pJCBpcyB0aGUgaW50ZW5zaXR5IG9mIGEgZ2l2ZW4KcHJvYmUgZnJvbSBjaGlwICRpJCBhbmQgWCRfaiQgaXMgdGhlIGludGVuc2l0eSBmb3IgdGhlIHNhbWUgcHJvYmUgb24gY2hpcCAkaiQsIHRoZW4gCiRBID0gMS8yIChsb2dfMihYX2kpICsgbG9nXzIoWF9qKSkkIGFuZCAkTSA9IGxvZ18yKFhfaSkg4oiSIGxvZ18yKFhfaikkIC4gSW4gdGhpcyBleHBlcmltZW50LCB0aGVyZSBhcmUgOCBHZW5lQ2hpcHMsIHdoaWNoIGdpdmVzCjI4IGRpc3RpbmN0IHBhaXItd2lzZSBjb21wYXJpc29ucyBiZXR3ZWVuIGFycmF5cy4gV2Ugd2lsbCBmb2N1cyBvbiBhIHN1YnNldCBvZiB0aGVzZSBiZWxvdy4KCmBgYHtyfQptdmEucGFpcnMocG0ocmF3KVssMTo0XSxwbG90Lm1ldGhvZD0ic21vb3RoU2NhdHRlciIpCm12YS5wYWlycyhwbShyYXcpWyw1OjhdLHBsb3QubWV0aG9kPSJzbW9vdGhTY2F0dGVyIikKYGBgCgoKKioqKioqCkJhc2VkIG9uIGFsbCB0aGUgcGxvdHMgeW91IGhhdmUgZ2VuZXJhdGVkLCB3aGF0IHdvdWxkIHlvdSBjb25jbHVkZSBhYm91dCB0aGUgb3ZlcmFsbCBxdWFsaXR5IG9mIHRoaXMgZXhwZXJpbWVudD8gCioqKioqKgoKCgojIyBQcm9iZS1sZXZlbCBMaW5lYXIgTW9kZWxzCgpQcm9iZS1sZXZlbCBMaW5lYXIgTW9kZWxzIChQTE1zKSBjYW4gYmUgdXNlZCBhcyBhbiBhZGRpdGlvbmFsIHRvb2wgdG8gYXNzZXNzIHJlbGF0aXZlCmRhdGEgcXVhbGl0eSB3aXRoaW4gYW4gZXhwZXJpbWVudC4gTWFueSBkaWZmZXJlbnQgbW9kZWwgc3BlY2lmaWNhdGlvbnMgYXJlIHBvc3NpYmxlLCB3aXRoCnRoZSBzaW1wbGVzdCBmaXR0aW5nIGNoaXAsIGFuZCBwcm9iZSBlZmZlY3RzIHRvIHRoZSBsb2ckXzIkIGludGVuc2l0aWVzIHdpdGhpbiBlYWNoIHByb2Jlc2V0IGFjcm9zcwphbiBleHBlcmltZW50IGluIGEgcm9idXN0IHdheS4KVGhlIG91dHB1dCBpcyBhIG1hdHJpeCBvZiByZXNpZHVhbHMsIG9yIHdlaWdodHMgZm9yIGVhY2ggY2hpcCB3aGljaCBjYW4gYmUgdXNlZCBhcyBhbiBhZGRpdGlvbmFsIGRpYWdub3N0aWM7IHN5c3RlbWF0aWNhbGx5IGhpZ2ggcmVzaWR1YWxzIGFjcm9zcyBhbiBlbnRpcmUgYXJyYXksIG9yIGEgbGFyZ2UgZnJhY3Rpb24gb2YgYW4gYXJyYXkgaXMgaW5kaWNhdGl2ZSBvZiBhbiBvdXRsaWVyIGFycmF5LiBUaGUgbWFpbiB1c2Ugb2YgdGhpcyB0b29sIGlzIGluIGRlY2lkaW5nIHdoZXRoZXIgb3IKbm90IHRvIGtlZXAgYW4gYXJyYXkgaW4gdGhlIGRvd24tc3RyZWFtIGRhdGEgYW5hbHlzaXMuCgojIyMgUmVsYXRpdmUgTG9nIEV4cHJlc3Npb24gKFJMRSkKClRoZSBSZWxhdGl2ZSBMb2cgRXhwcmVzc2lvbiAoUkxFKSB2YWx1ZXMgYXJlIGNvbXB1dGVkIGJ5IGNhbGN1bGF0aW5nIGZvciBlYWNoIHByb2JlLXNldCB0aGUgcmF0aW8gYmV0d2VlbiB0aGUgZXhwcmVzc2lvbiBvZiBhIHByb2JlLXNldCBhbmQgdGhlIG1lZGlhbiBleHByZXNzaW9uIG9mIHRoaXMgcHJvYmUtc2V0IGFjcm9zcyBhbGwgYXJyYXlzIG9mIHRoZSBleHBlcmltZW50LiBJdCBpcyBhc3N1bWVkIHRoYXQgbW9zdCBwcm9iZS1zZXRzIGFyZSBub3QgY2hhbmdlZCBhY3Jvc3MgdGhlIGFycmF5cywgc28gaXQgaXMgZXhwZWN0ZWQgdGhhdCB0aGVzZSByYXRpb3MgYXJlIGFyb3VuZCAwIG9uIGEgbG9nIHNjYWxlLiBUaGUgYm94cGxvdHMgcHJlc2VudGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIGxvZy1yYXRpb3Mgc2hvdWxkIHRoZW4gYmUgY2VudGVyZWQgbmVhciAwIGFuZCBoYXZlIHNpbWlsYXIgc3ByZWFkLiBPdGhlciBiZWhhdmlvciB3b3VsZCBiZSBhIHNpZ24gb2YgbG93IHF1YWxpdHkuCgoKIyMjIE5vcm1hbGl6ZWQgVW5zY2FsZWQgU3RhbmRhcmQgRXJyb3IgKE5VU0UpIAoKVGhlIE5vcm1hbGl6ZWQgVW5zY2FsZWQgU3RhbmRhcmQgRXJyb3IgKE5VU0UpIGlzIHRoZSBpbmRpdmlkdWFsIHByb2JlIGVycm9yIGZpdHRpbmcgdGhlIFByb2JlLUxldmVsIE1vZGVsICh0aGUgUExNIG1vZGVscyBleHByZXNzaW9uIG1lYXN1cmVzIHVzaW5nIGEgTS1lc3RpbWF0b3Igcm9idXN0IHJlZ3Jlc3Npb24pLiBUaGUgTlVTRSB2YWx1ZXMgYXJlIHN0YW5kYXJkaXplZCBhdCB0aGUgcHJvYmUtc2V0IGxldmVsIGFjcm9zcyB0aGUgYXJyYXlzOiBtZWRpYW4gdmFsdWVzIGZvciBlYWNoIHByb2JlLXNldCBhcmUgc2V0IHRvIDEuIFRoZSBib3hwbG90cyBhbGxvdyBjaGVja2luZyAoMSkgaWYgYWxsIGRpc3RyaWJ1dGlvbnMgYXJlIGNlbnRlcmVkIG5lYXIgMSAodHlwaWNhbGx5IGFuIGFycmF5IHdpdGggYSBib3hwbG90IGNlbnRlcmVkIGFyb3VuZCAxLjEgc2hvd3MgYmFkIHF1YWxpdHkpIGFuZCAoMikgaWYgb25lIGFycmF5IGhhcyBnbG9iYWxseSBoaWdoZXIgc3ByZWFkIG9mIE5VU0UgZGlzdHJpYnV0aW9uIHRoYW4gb3RoZXJzLCB3aGljaCBtYXkgYWxzbyBiZSBhIHNpZ24gb2YgbG93IHF1YWxpdHkuCgpgYGB7cn0KbGlicmFyeShhZmZ5UExNKQpwbG1zZXQgPC0gZml0UExNKHJhdykKTlVTRShwbG1zZXQsbGFzPTIpClJMRShwbG1zZXQsbGFzPTIpCmBgYAoKCldlIGNhbiBsb29rIGF0IHRoZSBpbWFnZXMgb2YgdGhlIGFycmF5IHN1cmZhY2UuIE9jYXNzaW9uYWxseSB0aGlzIGNhbiByZXZlYWwgcHJvYmxlbXMgb24gdGhlIGFycmF5LiBTZWUgdGhpcyBbZ2FsbGVyeV0oaHR0cDovL3BsbWltYWdlZ2FsbGVyeS5ibWJvbHN0YWQuY29tLykgb2YgaW50ZXJlc3RpbmcgZXhhbXBsZXMuIEFmZnltZXRyaXgsIGFuZCBvdGhlciBvbGRlciBhcnJheXMgYXJlIHZ1bmVyYWJsZSB0byBzcGF0aWFsIGFydGVmYWN0cyBhcyB0aGUgc2FtZSBwcm9iZSBzZXQgaXMgZm91bmQgaW4gdGhlIHNhbWUgbG9jYXRpb24gb24gZWFjaCBjaGlwLiAKCklmIHlvdSB3YW50IHRvIGxvb2sgYXQgYW4gZXhhbXBsZSBvZiBhIGJhZCBhcnJheSBpbWFnZSwgd2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvZGU7CgpgYGB7cn0KYmFkIDwtIFJlYWRBZmZ5KGNlbGZpbGUucGF0aCA9ICJlc3Ryb2dlbi8iLGZpbGVuYW1lcz0iYmFkLmNlbCIpCmltYWdlKGJhZCkKYGBgCgpUcnkgb3V0IHNvbWUgaW1hZ2VzIGZvciB0aGlzIGRhdGFzZXQKCmBgYHtyfQpwYXIobWZyb3c9YygyLDQpKQppbWFnZShyYXdbLDFdKQppbWFnZShyYXdbLDJdKQppbWFnZShyYXdbLDNdKQppbWFnZShyYXdbLDRdKQppbWFnZShyYXdbLDVdKQppbWFnZShyYXdbLDZdKQppbWFnZShyYXdbLDddKQppbWFnZShyYXdbLDhdKQpgYGAKCgotIERvIHlvdSBzZWUgYW55IHByb2JsZW1zPwoKSWYgd2UgYXJlIGhhcHB5IHdpdGggdGhlIHF1YWxpdHkgb2YgdGhlIHJhdyBkYXRhIHdlIGNhbiBwcm9jZWVkIHRvIHRoZSBuZXh0IHN0ZXAuIFNvIGZhciBpbiB0aGUgZGF0YSwgd2UgaGF2ZSBtdWx0aXBsZSBwcm9iZXMgd2l0aGluIGEgcHJvYmVzZXQsIGFuZCBhIHBlcmZlY3QgYW5kIG1pc21hdGNoIG1lYXN1cmVtZW50IGZvciBlYWNoIHByb2JlLiBUaGVzZSBhcmUgbm90IHBhcnRpY3VsYXIgY292ZW5pZW50IHZhbHVlcyBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgYXMgd2Ugd291bGQgbGlrZSBhICpzaW5nbGUgbWVhc3VyZW1lbnQqIGZvciBlYWNoIGdlbmUgZm9yIGVhY2ggc2FtcGxlICgvaHlicmlkaXNhdGlvbikuIAoKIyMgU3VtbWFyaXNpbmcgYW5kIE5vcm1hbGlzaW5nIHRoZSBFc3Ryb2dlbiBkYXRhc2V0CgpNYW55IG5vcm1hbGlzYXRpb24gYW5kIHN1bW1hcmlzYXRpb24gbWV0aG9kcyBoYXZlIGJlZW4gZGV2ZWxvcGVkIGZvciBBZmZ5bWV0cml4CmRhdGEuIFRoZXNlIGluY2x1ZGUgTUFTNS4wIGFuZCBQTElFUiB3aGljaCB3ZXJlIGRldmVsb3BlZCBieSBBZmZ5bWV0cml4LCBhbmQgUk1BLApHQy1STUEsIGRDaGlwIGFuZCB2c24gKHRvIG5hbWUgYnV0IGEgZmV3KSB3aGljaCBoYXZlIGJlZW4gZGV2ZWxvcGVkIGJ5IGFjYWRlbWljIHJlc2VhcmNoZXJzLgpNYW55IG9mIHRoZXNlIG1ldGhvZHMgYXJlIGF2YWlsYWJsZSBpbiB0aGUgW2FmZnldKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2FmZnkuaHRtbCkgcGFja2FnZS4gRm9yIGEgY29tcGFyaXNvbiBvZiBzb21lIG9mIHRoZXNlCm1ldGhvZHMgYW5kIGFzc2Vzc21lbnQgb2YgdGhlaXIgcGVyZm9ybWFuY2Ugb24gZGlmZmVyZW50IGNvbnRyb2wgZGF0YSBzZXRzLCBzZWUgW0JvbHN0YWQgZXQgYWxdKGh0dHA6Ly9iaW9pbmZvcm1hdGljcy5veGZvcmRqb3VybmFscy5vcmcvY29udGVudC8xOS8yLzE4NS5sb25nKSBvciBbTWlsbGVuYWFyIGV0IGFsXShodHRwOi8vYm1jYmlvaW5mb3JtYXRpY3MuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDcxLTIxMDUtNy0xMzcpLiBJbgp0aGlzIHByYWN0aWNhbCB3ZSB3aWxsIHVzZSB0aGUgKioqUk1BKioqIChSb2J1c3QgTXVsdGljaGlwIEF2ZXJhZ2luZykgbWV0aG9kIGRlc2NyaWJlZCBpbiBbSXJpemFycnkgZXQgYWxdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMTI5MjU1MjApLgoKUHJvY2VkdXJlOwoKLSBtb2RlbCBiYXNlZCBiYWNrZ3JvdW5kIGFkanVzdG1lbnQgCi0gZm9sbG93ZWQgYnkgcXVhbnRpbGUgbm9ybWFsaXNhdGlvbiBhbmQgYSAKLSByb2J1c3Qgc3VtbWFyeSBtZXRob2QgKG1lZGlhbiBwb2xpc2gpIG9uIHRoZSBsb2ckXzIkIFBNIGludGVuc2l0aWVzIAotIHRvIG9idGFpbiAqcHJvYmVzZXQgc3VtbWFyeSB2YWx1ZXMqLgoKIyMgTm9ybWFsaXNhdGlvbgoKCi0gV2Ugd2FudCB0byBiZSBvYnNlcnZpbmcgKmJpb2xvZ2ljYWwqIGFuZCBub3QgKnRlY2huaWNhbCogdmFyaWF0aW9uCi0gV2Ugd291bGRuJ3QgZXhwZWN0IHN1Y2ggd2hvbGVzYWxlIGNoYW5nZXMgb24gYSBwZXItc2FtcGxlIGJhc2lzCi0gRWFzeSBvcHRpb24gd291bGQgdG8gc2NhbGUgdmFsdWVzIGZvciBlYWNoIGFycmF5IHRvIG1lZGlhbiBsZXZlbAoKLSAqKldoYXQgd291bGQgaGFwcGVuIGlmIHdlIGhhZCBoeWJyaWRpc2VkIGFsbCB0aGUgKmVzdHJvZ2VuLXRyZWF0ZWQqIHNhbXBsZXMgb24gdGhlIGZpcnN0IGZvdXIgYXJyYXlzLCBhbmQgKnVuLXRyZWF0ZWQqIG9uIHRoZSBsYXN0IGZvdXIqKiAtIENvdWxkIHlvdSBhbmFseXNlIHRoZSBkYXRhPwohW10oaW1hZ2VzL2xpbmVhci1lZmZlY3RzLnBuZykKCgotIEdlbmVzIG9uIGFycmF5IDIgYXJlIG9uIGF2ZXJhZ2UgMi42IGxvd2VyIHRoYW4gdGhlIGdsb2JhbCBtZWRpYW4sIHNvIGFkZCAyLjYgdG8gZWFjaCBnZW5lCi0gR2VuZXMgb24gYXJyYXkgOCBhcmUgb24gYXZlcmFnZSAwLjcgaGlnaGVyIHRoYW4gdGhlIGdsb2JhbCBtZWRpYW4sIHNvIHN1YnRyYWN0IDAuNyBmcm9tIGVhY2ggZ2VuZQotIGV0YwoKCgojIyBOb24tbGluZWFyIGVmZmVjdHMKCkFzIHdlIHNhdyBiZWZvcmUgVGhlICpNQS1wbG90KiBpcyBjb21tb25seS11c2VkIGluIG1pY3JvYXJyYXkgYW5hbHlzaXMgdG8gY29tbW9ubHktdXNlZCB0byB2aXN1YWxpc2UgZGlmZmVyZW5jZXMgYmV0d2VlbiBwYWlycyBvZiBhcnJheXMuIFRoZXNlIHBsb3RzIGNhbiByZXZlYWwgbm9uLWxpbmVhciBlZmZlY3RzIGFuZCBtb3RpdmF0ZSBtb3JlLXNvcGhpc3RpY2F0ZWQgbWV0aG9kcy4gT24gb2xkZXIgYXJyYXkgdGVjaG5vbG9naWVzLCBpdCB3YXMgdHlwaWNhbCB0byBzZWUgYSBiYW5hbmEtc2hhcGVkIHRyZW5kIG9uIHRoZXNlIHBsb3RzLgoKKCoqKm5vdCBvdXIgZGF0YXNldCoqKikKCiFbXShpbWFnZXMvbm9uLWxpbmVhci5wbmcpCgoKIyMgUXVhbnRpbGUgbm9ybWFsaXNhdGlvbgoKVGhpcyBpcyBhcmd1YWJseSB0aGUgbW9zdC1wb3B1bGFyIG5vcm1hbGlzYXRpb24gbWV0aG9kIGF2YWlsYWJsZS4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBtYXRyaXggb2YgdmFsdWVzIHRvIGJlIG5vcm1hbGlzZWQKCmBgYHtyIGVjaG89RkFMU0V9CiMgU2V0IGEgc2VlZCB0byBtYWtlIHN1cmUgd2UgZ2V0IHRoZSBzYW1lIG1hdHJpeApzZXQuc2VlZCgiMTUwMjIwMTYiKQpyYXcudmFsdWVzIDwtIHJvdW5kKGRhdGEuZnJhbWUoQXJyYXkxID0gcm5vcm0oNSxtZWFuID0gMiksIEFycmF5MiA9IHJub3JtKDUsbWVhbj0zKSwgQXJyYXkzID0gcm5vcm0oNSxtZWFuPTQpKSwyKQpyb3duYW1lcyhyYXcudmFsdWVzKSA8LSBMRVRURVJTWzE6bnJvdyhyYXcudmFsdWVzKV0KcmF3LnZhbHVlcwpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFfQpyYW5rZWQudmFsdWVzIDwtIGFwcGx5KHJhdy52YWx1ZXMsIDIsIGZ1bmN0aW9uKHgpIHBhc3RlKCJSYW5rIixyYW5rKHgsdGllcy5tZXRob2Q9Im1pbiIpLHNlcD0iIikpCnJhbmtlZC52YWx1ZXMKYGBgCgpTb3J0IGVhY2ggY29sdW1uIFNtYWxsZXN0Li4uTGFyZ2VzdAoKU29ydGVkIGRhdGEKYGBge3J9CmFwcGx5KHJhdy52YWx1ZXMsIDIsc29ydCkKYGBgCgpUaGVuIGNhbGN1bGF0ZSB0YXJnZXQgZGlzdHJpYnV0aW9uIGJ5IGF2ZXJhZ2luZyB0aGUgc29ydGVkIHJvd3MKYGBge3IgZWNobz1GQUxTRX0gCnRhcmdldCA8LSByb3VuZChyb3dNZWFucyhhcHBseShyYXcudmFsdWVzLCAyLHNvcnQpKSwzKQpuYW1lcyh0YXJnZXQpIDwtIHBhc3RlKCJSYW5rIiwgMTpsZW5ndGgodGFyZ2V0KSxzZXA9IiIpCnRhcmdldApgYGAKCkdvIGJhY2sgdG8gdGhlIHJhbmsgbWF0cml4CgpgYGB7ciBlY2hvPUZBTFNFfQpyYW5rZWQudmFsdWVzCgpgYGAKU3Vic3RpdHV0ZSB3aXRoIHZhbHVlcyBmcm9tIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uCmBgYHtyIGVjaG89RkFMU0V9CnRhcmdldApgYGAKCmBgYHtyfQpyYW5rZWQudmFsdWVzWywxXSA8LSBnc3ViKCJSYW5rMSIsdGFyZ2V0WyJSYW5rMSJdLHJhbmtlZC52YWx1ZXNbLDFdKQpyYW5rZWQudmFsdWVzCnJhbmtlZC52YWx1ZXNbLDFdIDwtIGdzdWIoIlJhbmsyIix0YXJnZXRbIlJhbmsyIl0scmFua2VkLnZhbHVlc1ssMV0pCnJhbmtlZC52YWx1ZXMKcmFua2VkLnZhbHVlc1ssMV0gPC0gZ3N1YigiUmFuazMiLHRhcmdldFsiUmFuazMiXSxyYW5rZWQudmFsdWVzWywxXSkKcmFua2VkLnZhbHVlcwpyYW5rZWQudmFsdWVzWywxXSA8LSBnc3ViKCJSYW5rNCIsdGFyZ2V0WyJSYW5rNCJdLHJhbmtlZC52YWx1ZXNbLDFdKQpyYW5rZWQudmFsdWVzCnJhbmtlZC52YWx1ZXNbLDFdIDwtIGdzdWIoIlJhbms1Iix0YXJnZXRbIlJhbms1Il0scmFua2VkLnZhbHVlc1ssMV0pCnJhbmtlZC52YWx1ZXMKYGBgCgoKYGBge3J9CmZvcihpIGluIDE6Myl7CnJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIoIlJhbmsxIix0YXJnZXRbIlJhbmsxIl0scmFua2VkLnZhbHVlc1ssaV0pCnJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIoIlJhbmsyIix0YXJnZXRbIlJhbmsyIl0scmFua2VkLnZhbHVlc1ssaV0pCnJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIoIlJhbmszIix0YXJnZXRbIlJhbmszIl0scmFua2VkLnZhbHVlc1ssaV0pCnJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIoIlJhbms0Iix0YXJnZXRbIlJhbms0Il0scmFua2VkLnZhbHVlc1ssaV0pCnJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIoIlJhbms1Iix0YXJnZXRbIlJhbms1Il0scmFua2VkLnZhbHVlc1ssaV0pCn0KcmFua2VkLnZhbHVlcyA8LSBhcy5kYXRhLmZyYW1lKHJhbmtlZC52YWx1ZXMpCnJvd25hbWVzKHJhbmtlZC52YWx1ZXMpIDwtIHJvd25hbWVzKHJhdy52YWx1ZXMpCgpgYGAKCmBgYHtyfQpyYXcudmFsdWVzCnJhbmtlZC52YWx1ZXMKYGBgCgoKIyNGaW5hbCBDb2RlCgpUaGUgcHJvY2VkdXJlIGNhbiBiZSBlbmNhcHN1bGF0ZWQgaW4gcmVsYXRpdmVseSBmZXcgbGluZXMgb2YgY29kZQoKYGBge3IgZXZhbD1GQUxTRX0KCnJhbmtlZC52YWx1ZXMgPC0gYXBwbHkocmF3LnZhbHVlcywgMiwgZnVuY3Rpb24oeCkgcGFzdGUoIlJhbmsiLAogICAgICByYW5rKHgsdGllcy5tZXRob2Q9Im1pbiIpLHNlcD0iIikpCnRhcmdldCA8LSByb3VuZChyb3dNZWFucyhhcHBseShkZiwgMixzb3J0KSksMykKbmFtZXModGFyZ2V0KSA8LSBwYXN0ZSgiUmFuayIsIDE6bGVuZ3RoKHRhcmdldCksc2VwPSIiKQpmb3IoaSBpbiAxOm5jb2wocmF3LnZhbHVlcykpewogIGZvcihubSBpbiBuYW1lcyh0YXJnZXQpKXsKICAgIHJhbmtlZC52YWx1ZXNbLGldIDwtIGdzdWIobm0sdGFyZ2V0W25tXSxyYW5rZWQudmFsdWVzWyxpXSkgIAogICAgICB9Cn0Kbm9ybSA8LSBhcy5kYXRhLmZyYW1lKHJhbmtlZC52YWx1ZXMpCmBgYAoKCgpXZSBjYW4gdXNlIHRoZSBgbm9ybWFsaXplUXVhbnRpbGVzYCBmdW5jdGlvbiB3aXRoaW4gdGhlIGBsaW1tYWAgcGFja2FnZSB0byBkbyB0aGUgbm9ybWFsaXNhdGlvbi4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGxpbW1hKQo/bm9ybWFsaXplUXVhbnRpbGVzCmBgYAoKIyMjIENhdmVhdHMKCi0gRGlzdHJpYnV0aW9ucyBvZiBzYW1wbGVzIGFyZSBleHBlY3RlZCB0byBiZSB0aGUgc2FtZQotIE1ham9yaXR5IG9mIGdlbmVzIGRvIG5vdCBjaGFuZ2UgYmV0d2VlbiBncm91cHMKICAgICsgbWlnaHQgYmUgdmlvbGF0ZWQgaWYgeW91IGhhdmUgc2FtcGxlcyB0aGF0IGFyZSBkcmFtYXRpY2FsbHkgYWx0ZXJlZAogICAgKyBlLmcuIGxvdyBudW1iZXIgb2YgZ2VuZXMgaW4gYSBzY3JlZW5pbmcgcGFuZWwgdGhhdCBhcmUgZXhwZWN0ZWQgdG8gY2hhbmdlCiAgICAKIyMgT3RoZXIgbm9ybWFsaXNhdGlvbiBwcm9jZWR1cmVzCgotIGxvZXNzIG9yIHNwbGluZXMKICAgICsgbmVlZCBhIHJlZmVyZW5jZSBvciAiYXZlcmFnZSIgYXJyYXkgdG8gY29tcGFyZSAKICAgICsgZXN0aW1hdGUgdGhlIGN1cnZlIGFuZCB1c2UgdG8gY29ycmVjdCBlYWNoIHBvaW50CiAgICAKCmBgYHtyfQpsaWJyYXJ5KGFmZnkpCj9ub3JtYWxpemUubG9lc3MKYGBgCgoKIyMgUnVubmluZyBSTUEgb24gb3VyIGRhdGFzZXQKClRoZSBhZmZ5IHBhY2thZ2UgcHJvdmlkZXMgbG90cyBvZiBkaWZmZXJlbnQgd2F5cyB0byBwcm9jZXNzIHJhdyBBZmZ5bWV0cmljeCBkYXRhLCBvbmUgb2Ygd2hpY2ggaXMgYW4gaW1wbGVtZW50YXRpb24gb2YgUk1BLiBGb3Igb3RoZXIgbWV0aG9kcywgc2VlIHRoZSBgZXhwcmVzc29gIGZ1bmN0aW9uIG9yIHBhY2thZ2UgKlZpZ25ldHRlKgoKCmBgYHtyIGV2YWw9RkFMU0V9Cj9leHByZXNzbwp2aWduZXR0ZSgiYWZmeSIpCmBgYAoKVGhlIHJlc3VsdCBpcyBhbiBgRXhwcmVzc2lvblNldGAsIHdoaWNoIGlzIGEgdWJpcXVpdG91cyBvYmplY3QgaW4gQmlvb2NvbmR1Y3RvciBmb3Igc3RvcmluZyBoaWdoLXRocm91Z2hwdXQgZGF0YS4gTGF0ZXItb24gaW4gdGhlIGNvdXJzZSB3aWxsIHdlIGltcG9ydCBkYXRhIGZyb20gcHVibGljIHJlcG9zaXRvcmllcywgYW5kIHRoZXNlIGRhdGEgd2lsbCBvZnRlbiBiZSBpbiBhIG5vcm1hbGlzZWQgZm9ybS4KCmBgYHtyfQplc2V0IDwtIHJtYShyYXcpCmVzZXQKYGBgCgpUaGUgYEV4cHJlc3Npb25TZXRgIGlzIGEgY29udmVuaWVudCBvYmplY3QtdHlwZSBmb3Igc3RvcmluZyBtdWx0aXBsZSBoaWdoLWRpbWVuc2lvbmFsIGRhdGEgZnJhbWVzLiBUaGUgc3RydWN0dXJlIG9mIHRoZSBvYmplY3QgaXMgbXVjaCBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gb3RoZXIgdHlwZXMgb2YgZGF0YSB3ZSBoYXZlIHNlZW4gYmVmb3JlLiBJbiBmYWN0IHdlIGRvIG5vdCBuZWVkIHRvIGtub3cgbmVlZCBleGFjdGx5ICpob3cqIHRoZSBvYmplY3QgaXMgY29uc3RydWN0ZWQgaW50ZXJuYWxseS4gVGhlIHBhY2thZ2UgYXV0aG9ycyBoYXZlIHByb3ZpZGVkIGNvbnZlbmllbnQgZnVuY3Rpb25zIHRoYXQgd2lsbCBhbGxvdyB1cyB0byBhY2Nlc3MgdGhlIGRhdGEuIAoKRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnQgdG8gZXh0cmFjdCB0aGUgZXhwcmVzc2lvbiBtZWFzdXJlbWVudHMgdGhlbXNlbHZlcyB0aGUgZnVuY3Rpb24gdG8gdXNlIGlzIGNhbGxlZCBgZXhwcnNgLiBUaGUgcmVzdWx0IHdpbGwgYmUgYSBtYXRyaXggd2l0aCBvbmUgcm93IGZvciBlYWNoIGdlbmUsIGFuZCBvbmUgY29sdW1uIGZvciBlYWNoIHNhbXBsZS4KCmBgYHtyfQpoZWFkKGV4cHJzKGVzZXQpKQpzdW1tYXJ5KGV4cHJzKGVzZXQpKQpgYGAKCllvdSB3aWxsIG5vdGljZSB0aGUgKnJtYSogYWxzbyBpbmNvcnBvcmF0ZXMgYSBsb2ckXzIkIHRyYW5zZm9ybWF0aW9uLiBUaGUgdmFsdWVzIHByZXNlbnQgaW4gdGhlICpjZWwqIGZpbGVzIGFyZSBkZXJpdmVkIGZyb20gcHJvY2Vzc2luZyB0aGUgKlRJRkYqIGltYWdlcyBvZiB0aGUgY2hpcCBzdXJmYWNlLCB3aGljaCBhcmUgb24gdGhlIHNjYWxlIDAgLSAyXjE2LiBIb3dldmVyLCB0aGlzIGlzIG5vdCBhIHZlcnkgY29udmVuaWVudCBzY2FsZSBmb3IgdmlzdWFsaXNhdGlvbiBhbmQgYW5hbHlzaXMgYXMgbW9zdCBvZiB0aGUgb2JzZXJ2YXRpb25zIGFyZSBmb3VuZCBhdCB0aGUgbG93ZXIgZW5kIG9mIHRoZSBzY2FsZSBhbmQgdGhlcmUgaXMgYSBtdWNoIGdyZWF0ZXIgdmFyaWFuY2UgYXQgdGhlIGhpZ2VyIHZhbHVlcy4gV2Ugc2F5IHRoYXQgdGhlIGRhdGEgZXhoaWJpdCBbJ2hldGVyb3NjZWRhc3RpY2l0eSddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0hldGVyb3NjZWRhc3RpY2l0eSkuIFRvIGFsbGV2aWF0ZSB0aGlzIHdlIGNhbiB1c2UgYSBkYXRhIHRyYW5zZm9ybWF0aW9uIHN1Y2ggYXMgbG9nJF8yJCBhbmQgYWZ0ZXJ3YXJkcyBvdXIgZGF0YSB3aWxsIGJlIGluIHRoZSByYW5nZSAwIHRvIDE2LiBBbm90aGVyIG1ldGhvZCB5b3UgbWF5IGNvbWUgYWNyb3NzIGlzIFt2c25dKGh0dHA6Ly9iaW9pbmZvcm1hdGljcy5veGZvcmRqb3VybmFscy5vcmcvY29udGVudC8xOC9zdXBwbF8xL1M5Ni5sb25nKS4gCgoKYGBge3J9CmJveHBsb3QoZXhwcnMoZXNldCksbGFzPTIpCmBgYAoKCioqKioqKgojIyMgSG93IGNhbiB5b3UgdGVsbCBmcm9tIHRoZSBib3hwbG90IHRoYXQgdGhlc2UgZGF0YSBoYXZlIGJlZW4gbm9ybWFsaXNlZD8KKioqKioqCgpBbiBNQS1wbG90IGNhbiBiZSByZXBlYXRlZCBvbiB0aGUgbm9ybWFsaXNlZCBkYXRhIHRvIHZlcmlmeSB0aGF0IHRoZSBub3JtYWxpc2F0aW9uIHByb2NlZHVyZSBoYXMgYmVlbiBzdWNlc3NmdWwuCgpgYGB7cn0KbXZhLnBhaXJzKGV4cHJzKGVzZXQpWywxOjRdLGxvZy5pdCA9IEZBTFNFLHBsb3QubWV0aG9kPSJzbW9vdGhTY2F0dGVyIikKYGBgCgoKCklmIHdlIGhhdmUgdXNlZCBhICp0YXJnZXRzKiBmaWxlIHRvIHJlYWQgdGhlIHJhdyBkYXRhLCB0aGlzIGluZm9ybWF0aW9uIHdpbGwgc3RvcmVkIGluIHRoZSBzdW1tYXJpc2VkIG9iamVjdC4gQ29tbW9ubHktcmVmZXJyZWQgdG8gYXMgInBoZW5vIGRhdGEiLCB0aGlzIGNhbiByZXRyaWV2ZWQgdXNpbmcgdGhlIGBwRGF0YWAgZnVuY3Rpb24uIEEgbmljZSBwcm9wZXJ0eSBvZiB0aGlzIGRhdGEgZnJhbWUgaXMgdGhhdCB0aGUgKnJvd3MqIGFyZSBhcnJhbmdlZCBpbiB0aGUgc2FtZSBvcmRlciBhcyB0aGUgKmNvbHVtbnMqIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeC4gaS5lLiBUaGUgZmlyc3QgY29sdW1uIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeCBpcyBkZXNjcmliZWQgaW4gdGhlIGZpcnN0IHJvdyBvZiB0aGUgcGhlbm8gZGF0YSBtYXRyaXgsIGFuZCBzbyBvbi4gCgpgYGB7cn0KaGVhZChwRGF0YShlc2V0KSkKY29sbmFtZXMoZXhwcnMoZXNldCkpCnJvd25hbWVzKHBEYXRhKGVzZXQpKQpgYGAKCgpDbHVzdGVyaW5nIHRlY2huaXF1ZXMgYW5kIFBDQSB0aGF0IHdlIHdpbGwgbWVldCBsYXRlciBpbiB0aGUgY291cnNlIGNhbiBhbHNvIGluZm9ybSBkZWNpc2lvbnMgYWJvdXQgZXhwZXJpbWVudCBxdWFsaXR5IGJ5IHByb3ZpZGluZyBhIHdheSBvZiB2aXN1YWxpbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHNhbXBsZSBncm91cHMuCgoKIVtdKGltYWdlcy9hZmZ5LWNsdXN0ZXJpbmcucG5nKQoKIVtdKGltYWdlcy9hZmZ5LXBjYS5wbmcpCgoKIyMgQXV0b21hdGVkIHByb2R1Y3Rpb24gb2YgUUMgcGxvdHMKClRoZSBgYXJyYXlRdWFsaXR5TWV0cmljc2AgcGFja2FnZSBpcyBhYmxlIHRvIHByb2R1Y2UgUUEgcGxvdHMgZnJvbSBzZXQgb2YgY2VsIGZpbGVzLCBvciBhIG5vcm1hbGlzZWQgZGF0YXNldC4KCmBgYHtyIGV2YWw9RkFMU0V9CmxpYnJhcnkoYXJyYXlRdWFsaXR5TWV0cmljcykKYXJyYXlRdWFsaXR5TWV0cmljcyhlc2V0KQpgYGAKCiMjIyBUaG91Z2h0cyBvbiBRdWFsaXR5IEFzc2Vzc21lbnQgCgotIEl0IGlzIGRpZmZpY3VsdCB0byBjb21lIHVwIHdpdGggZGVmaW5pdGl2ZSBydWxlcyBhYm91dCB3aGVuIHRvIHJlamVjdCBhIHNhbXBsZSBmcm9tIHRoZSBhbmFseXNpcwotIFRyeSBhbmQga2VlcCBhcyBtdWNoIGluZm9ybWF0aW9uIGZyb20gdGhlIGxhYgogICAgKyBSSU4gKFJOQSBJbnRlZ3JpdHkgTnVtYmVycyksIHR1bW91ciBjb21wb3NpdGlvbgotIElmIHdlIHRyeSBhbmQgcmVqZWN0IHNhbXBsZXMgZm9yIGxvb2tpbmcgZGlmZmVyZW50LCB3ZSBtaWdodCBiaWFzIG91ciByZXN1bHRzIGFuZCBtaXNzIGludGVyZXN0aW5nIGZpbmRpbmdzCi0gSWYgdGhlIG1ham9yaXR5IG9mIG91ciBzYW1wbGVzIGFyZSBwb29yIHF1YWxpdHksIHRoZSBiZXR0ZXIgcXVhbGl0eSBvbmVzIG1pZ2h0IHN0YW5kLW91dCBhcyBvdXRsaWVycyEKLSBUcnkgYW5kIGNvbGxlY3QgYXMgbWFueSBzYW1wbGVzIC8gcmVwbGljYXRlcyBhcyBwb3NzaWJsZSB0byBiZSB0b2xlcmFudCB0byB2YXJpYXRpb24KCiMjIFN1bW1hcnkKCi0gQWZmeSBkYXRhIGNvbWUgaW4gYC5jZWxgIGZpbGVzIHRoYXQgY2FuIGJlIGltcG9ydGVkIHdpdGggdGhlIGBhZmZ5YCBwYWNrYWdlCi0gUUMgY2hlY2tzIG9uIHRoZSByYXcgZGF0YSBpbmNsdWRlOwogICAgKyBjaGVjayB0aGUgY2hpcCBpbWFnZQogICAgKyBwcm9iZS1sZXZlbCBtb2RlbHMKICAgICsgYm94cGxvdHMKLSBBZmZ5IGRhdGEgbmVlZCB0byBiZSBzdW1tYXJpc2VkIGJlZm9yZSBmdXJ0aGVyIGFuYWx5c2lzCiAgICArIGBybWFgIChhbmQgdmFyaWFudHMgdGhlcmVvZikgaXMgbW9zdC1wb3B1bGFyCi0gQWZmeSBkYXRhIGNhbiBiZSBzdW1tYXJpc2VkIGludG8gYSBjb21tb24gQmlvY29uZHVjdG9yIG9iamVjdC10eXBlCiAgICArIFRoZSAiYEV4cHJlc3Npb25TZXRgIgotIFRoZSBgRXhwcmVzc2lvblNldGAsIG9yIGRlcml2YXRpZXMgdGhlcmVvZiwgaXMgdXNlZCB0aHJvdWdob3V0IEJpb2NvbmR1Y3RvciB0byByZXByZXNlbnQgYWxsIHR5cGVzIG9mIGhpZ2gtdGhyb3VnaHB1dCBkYXRhCiAgICArIG1ldGh5bGF0aW9uLCBDaElQLCBSTkEtc2VxCiAgICArIGJhc2ljYWxseSBhbnl0aGluZyB3aGVyZSB5b3UgY2FuIGhhdmUgc29tZSBraW5kIG9mIGdlbm9taWMgbWVhc3VyZSBhcyByb3dzLCBhbmQgc2FtcGxlcyBhcyBjb2x1bW5zCg==